package com.globallogic.training.javase.university.schedule;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.globallogic.training.javase.university.curriculum.Activity;
import com.globallogic.training.javase.university.curriculum.ActivityType;
import com.globallogic.training.javase.university.curriculum.Subject;
import com.globallogic.training.javase.university.member.Group;
import com.globallogic.training.javase.university.member.Instructor;

public class SchedulerImpl implements Scheduler {

	private static final String PROPERTIES_FILE = "scheduler.properties";

	private static final String AUDITORIUMS_FILE_PROPERTY = "auditoriums.file";

	private static final String GROUPS_FILE_PROPERTY = "groups.file";

	private static final String SUBJECTS_FILE_PROPERTY = "subjects.file";

	private static final String SCHEDULE_FILE_PROPERTY = "schedule.file";

	private Set<Activity> activities;

	private Set<Auditorium> auditoriums;

	public static void main(String[] args) throws Exception {

		File file = null;

		Properties props = loadProperties();

		file = new File(props.getProperty(AUDITORIUMS_FILE_PROPERTY));
		Set<Auditorium> auditoriums = readAuditoriums(file);

		file = new File(props.getProperty(GROUPS_FILE_PROPERTY));
		Set<Group> groups = readGroups(file);
		List<Group> groupList = new ArrayList<Group>(groups);

		file = new File(props.getProperty(SUBJECTS_FILE_PROPERTY));
		Set<Subject> subjects = readSubjects(file);
		int i = 1;
		Set<Activity> activities = new LinkedHashSet<Activity>();
		for (Subject subject : subjects) {
			Activity lecture = subject.createActivity(ActivityType.LECTURE,
					"Subj-" + i + "-Lec", "Topic");
			Activity seminar = subject.createActivity(ActivityType.SEMINAR,
					"Subj-" + i + "-Sem", "Topic");
			i++;
			activities.add(lecture);
			activities.add(seminar);
			subject.enroll(groupList.get(i % groups.size()));
		}

		Scheduler scheduler = new SchedulerImpl();
		scheduler.setAuditoriums(auditoriums);
		scheduler.setActivities(activities);

		Schedule schedule = scheduler.buildSchedule();

		file = new File(props.getProperty(SCHEDULE_FILE_PROPERTY));
		PrintWriter writer = new PrintWriter(file);
		printSchedule(writer, schedule);
		writer.close();
	}

	private static Properties loadProperties() throws IOException {

		InputStream is = SchedulerImpl.class.getClassLoader()
				.getResourceAsStream(PROPERTIES_FILE);
		Properties props = new Properties();
		props.load(is);
		is.close();
		return props;
	}

	private static void printSchedule(PrintWriter writer, Schedule schedule) {

		int columns = schedule.getGroups().size();
		int rows = DayOfWeek.values().length * TimeSlot.values().length;
		ScheduleTable table = new ScheduleTable(columns, rows);

		int i = 0;
		for (Group group : schedule.getGroups()) {
			table.appendColumnCaption(i++, group.getId());
		}

		for (DayOfWeek dayOfWeek : DayOfWeek.values()) {

			for (TimeSlot timeSlot : TimeSlot.values()) {
				int row = dayOfWeek.ordinal() * TimeSlot.values().length
						+ timeSlot.ordinal();
				table.appendRowCaption(row,
						timeSlot.ordinal() == 0 ? dayOfWeek.name() : "");

				int column = 0;
				for (Group group : schedule.getGroups()) {
					ScheduledActivity activity = schedule.getScheduledActivity(
							group, dayOfWeek, timeSlot);
					if (activity != null) {
						table.appendLine(column, row, activity.getSubject()
								.getTitle());
						table.appendLine(column, row, activity.getTopic());
						table.appendLine(column, row, activity.getInstructor()
								.toString());
						table.appendLine(column, row, activity.getAuditorium()
								.toString());
					}
					column++;
				}
			}
		}

		writer.print(table.toString());
	}

	public static Set<Auditorium> readAuditoriums(File file) throws IOException {

		Set<Auditorium> result = new LinkedHashSet<Auditorium>();

		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);

		for (String line = null; (line = br.readLine()) != null;) {
			String[] auditoriumString = line.split("(\\s)*,(\\s)*");
			for (String string : auditoriumString) {
				result.add(new Auditorium(string));
			}
		}

		br.close();

		return result;
	}

	public static Set<Subject> readSubjects(File file) throws IOException {
		Set<Subject> result = new LinkedHashSet<Subject>();

		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);

		int i = 1;
		for (String line = null; (line = br.readLine()) != null;) {

			String id = String.format("subj%d", i);
			String title = StringUtils.substringBefore(line, "-").trim();
			Subject subject = new Subject(id, title);
			i++;

			String instructorsLine = StringUtils.substringAfter(line, "-")
					.trim();
			String[] instructors = instructorsLine.split("(\\s)*,(\\s)*");
			for (String instructor : instructors) {
				subject.addInstructor(new Instructor(instructor, new Date()));
			}

			result.add(subject);
		}

		br.close();

		return result;
	}

	public static Set<Group> readGroups(File file) throws IOException {
		Set<Group> result = new LinkedHashSet<Group>();

		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);

		for (String line = null; (line = br.readLine()) != null;) {
			String[] groupIds = line.split("(\\s)*,(\\s)*");
			for (String groupId : groupIds) {
				result.add(new Group(groupId));
			}
		}

		br.close();

		return result;
	}

	@Override
	public void setActivities(Set<Activity> activity) {
		this.activities = activity;
	}

	@Override
	public void setAuditoriums(Set<Auditorium> auditoriums) {
		this.auditoriums = auditoriums;
	}

	@Override
	public Schedule buildSchedule() {
		Schedule schedule = new Schedule();
		for (Activity activity : activities) {
			schedule.scheduleActivity(activity, auditoriums);
		}
		return schedule;
	}
}
